﻿using Autodesk.Revit.DB;
using System;
using System.Linq;
using System.Collections.Generic;
using static demo.GeometryCreation;
using demo.Extensions;

namespace demo
{
    public class Profile
    {
        public Profile(List<Curve> curves)
        {
            CurveLoop = CurveLoop.Create(curves);
        }
        public Profile(CurveLoop curveLoop)
        {
            CurveLoop = curveLoop;
        }
        /// <summary>
        /// 当前轮廓的内轮廓，默认为NULL
        /// </summary>
        public Profile InnerProfile { get; set; }



        /// <summary>
        /// 轮廓的闭合环
        /// </summary>
        public CurveLoop CurveLoop { get; set; }

        /// <summary>
        /// 创建方块
        /// </summary>
        /// <param name="center"></param>
        /// <param name="l"></param>
        /// <returns></returns>
        public Solid CreateBox(XYZ center, double l, XYZ extrusiondir = null)
        {
            var LHalf = l / 2;
            List<CurveLoop> curves = new List<CurveLoop>();
            CurveLoop curve = new CurveLoop();
            curve.Append(Line.CreateBound(new XYZ(center.X - LHalf, center.Y - LHalf, center.Z - LHalf), new XYZ(center.X - LHalf, center.Y + LHalf, center.Z - LHalf)));
            curve.Append(Line.CreateBound(new XYZ(center.X - LHalf, center.Y + LHalf, center.Z - LHalf), new XYZ(center.X + LHalf, center.Y + LHalf, center.Z - LHalf)));
            curve.Append(Line.CreateBound(new XYZ(center.X + LHalf, center.Y + LHalf, center.Z - LHalf), new XYZ(center.X + LHalf, center.Y - LHalf, center.Z - LHalf)));
            curve.Append(Line.CreateBound(new XYZ(center.X + LHalf, center.Y - LHalf, center.Z - LHalf), new XYZ(center.X - LHalf, center.Y - LHalf, center.Z - LHalf)));
            curves.Add(curve);
            if (extrusiondir == null)
            {
                extrusiondir = XYZ.BasisZ;
            }
            return GeometryCreationUtilities.CreateExtrusionGeometry(curves, extrusiondir, l);
        }

        /// <summary>
        /// 创建球型
        /// </summary>
        /// <param name="center">圆心</param>
        /// <param name="radius">半径</param>
        /// <returns></returns>
        public Solid CreateSphere(XYZ center, double radius)
        {
            Frame frame = new Frame(center, XYZ.BasisX, XYZ.BasisY, XYZ.BasisZ);
            List<CurveLoop> curves = new List<CurveLoop>();
            CurveLoop curve = new CurveLoop();
            var ellipse = Ellipse.CreateCurve(center, radius, radius, XYZ.BasisX, XYZ.BasisZ, -Math.PI / 2.0, Math.PI / 2.0);
            curve.Append(ellipse);
            curve.Append(Line.CreateBound(new XYZ(center.X, center.Y, center.Z + radius), new XYZ(center.X, center.Y, center.Z - radius)));
            curves.Add(curve);
            return GeometryCreationUtilities.CreateRevolvedGeometry(frame, curves, -Math.PI, Math.PI);
        }

        /// <summary>
        /// 圆柱体
        /// </summary>
        /// <param name="center"></param>
        /// <param name="bottomradius"></param>
        /// <param name="height"></param>
        /// <param name="cylinderdirection"></param>
        /// <returns></returns>
        public Solid CreateCylinder(XYZ center, double bottomradius, double height, CylinderDirection cylinderdirection)
        {
            double halfheight = height / 2.0;
            XYZ bottomcenter = new XYZ(
               cylinderdirection == CylinderDirection.BasisX ? center.X - halfheight : center.X,
               cylinderdirection == CylinderDirection.BasisY ? center.Y - halfheight : center.Y,
               cylinderdirection == CylinderDirection.BasisZ ? center.Z - halfheight : center.Z);
            XYZ topcenter = new XYZ(
               cylinderdirection == CylinderDirection.BasisX ? center.X + halfheight : center.X,
               cylinderdirection == CylinderDirection.BasisY ? center.Y + halfheight : center.Y,
               cylinderdirection == CylinderDirection.BasisZ ? center.Z + halfheight : center.Z);

            CurveLoop sweepPath = new CurveLoop();
            sweepPath.Append(Line.CreateBound(bottomcenter, topcenter));

            List<CurveLoop> profileloops = new List<CurveLoop>();
            CurveLoop profileloop = new CurveLoop();
            var cemiEllipse1 = Ellipse.CreateCurve(bottomcenter, bottomradius, bottomradius,
               cylinderdirection == CylinderDirection.BasisX ? Autodesk.Revit.DB.XYZ.BasisY : Autodesk.Revit.DB.XYZ.BasisX,
               cylinderdirection == CylinderDirection.BasisZ ? Autodesk.Revit.DB.XYZ.BasisY : Autodesk.Revit.DB.XYZ.BasisZ,
               -Math.PI, 0);
            var cemiEllipse2 = Ellipse.CreateCurve(bottomcenter, bottomradius, bottomradius,
               cylinderdirection == CylinderDirection.BasisX ? Autodesk.Revit.DB.XYZ.BasisY : Autodesk.Revit.DB.XYZ.BasisX,
               cylinderdirection == CylinderDirection.BasisZ ? Autodesk.Revit.DB.XYZ.BasisY : Autodesk.Revit.DB.XYZ.BasisZ,
               0, Math.PI);
            profileloop.Append(cemiEllipse1);
            profileloop.Append(cemiEllipse2);
            profileloops.Add(profileloop);

            return GeometryCreationUtilities.CreateSweptGeometry(sweepPath, 0, 0, profileloops);
        }


        /// <summary>
        /// 平移的轮廓
        /// </summary>
        /// <param name="xoffset"></param>
        /// <param name="yoffset"></param>
        /// <returns></returns>
        public Profile TranslatedProfile(double xoffset, double yoffset)
        {
            Transform transform = Transform.CreateTranslation(new XYZ(xoffset, yoffset, 0));
            Profile profile = new Profile(CurveLoop.CreateViaTransform(CurveLoop, transform));
            if (InnerProfile != null)
            {
                profile.InnerProfile = new Profile(CurveLoop.CreateViaTransform(InnerProfile.CurveLoop, transform));
            }
            return profile;
        }
        /// <summary>
        /// 旋转的轮廓
        /// </summary>
        /// <param name="angle"></param>
        /// <returns></returns>
        public Profile RotatedProfile(double angle)
        {
            Transform transform = Transform.CreateRotation(XYZ.BasisZ, angle);
            Profile profile = new Profile(CurveLoop.CreateViaTransform(CurveLoop, transform));
            if (InnerProfile != null)
            {
                profile.InnerProfile = new Profile(CurveLoop.CreateViaTransform(InnerProfile.CurveLoop, transform));
            }
            return profile;
        }
        /// <summary>
        /// 沿Y轴镜像的轮廓
        /// </summary>
        public Profile YMirroredProfile()
        {
            Transform transform = Transform.CreateReflection(Plane.CreateByNormalAndOrigin(XYZ.BasisX, XYZ.Zero));
            Profile profile = new Profile(CurveLoop.CreateViaTransform(CurveLoop, transform));
            if (InnerProfile != null)
            {
                profile.InnerProfile = new Profile(CurveLoop.CreateViaTransform(InnerProfile.CurveLoop, transform));
            }
            return profile;
        }
        /// <summary>
        /// 沿X轴镜像的轮廓
        /// </summary>
        public Profile XMirroredProfile()
        {
            Transform transform = Transform.CreateReflection(Plane.CreateByNormalAndOrigin(XYZ.BasisY, XYZ.Zero));
            Profile profile = new Profile(CurveLoop.CreateViaTransform(CurveLoop, transform));
            if (InnerProfile != null)
            {
                profile.InnerProfile = new Profile(CurveLoop.CreateViaTransform(InnerProfile.CurveLoop, transform));
            }
            return profile;
        }

        /// <summary>
        /// 当前平面视图创建详图线,放在事务内
        /// </summary>
        /// <param name="document"></param>
        /// <param name="view">平面视图</param>
        /// <returns></returns>
        public virtual List<ElementId> CreateDetailCurve(Document document)
        {
            if (document.ActiveView is ViewPlan)
            {
                List<ElementId> eids = new List<ElementId>();
                foreach (var curve in CurveLoop)
                {
                    DetailCurve detail = document.Create.NewDetailCurve(document.ActiveView, curve);
                    eids.Add(detail.Id);
                }
                if (InnerProfile != null)
                {
                    foreach (var curve in InnerProfile.CurveLoop)
                    {
                        DetailCurve detail = document.Create.NewDetailCurve(document.ActiveView, curve);
                        eids.Add(detail.Id);
                    }
                }
                return eids;
            }
            else
            {
                return null;
            }
        }
        /// <summary>
        /// 构建圆轮廓，原点为中心点
        /// </summary>
        /// <param name="radius"></param>
        /// <returns></returns>
        public static Profile CreateCircle(double radius)
        {
            Plane plane = Plane.CreateByNormalAndOrigin(XYZ.BasisZ, XYZ.Zero);
            Arc arc1 = Arc.Create(plane, radius, 0, Math.PI);
            Arc arc2 = Arc.Create(plane, radius, Math.PI, 2 * Math.PI);
            CurveLoop loop = new CurveLoop();
            loop.Append(arc1); loop.Append(arc2);
            return new Profile(loop);
        }
        /// <summary>
        /// 构建矩形轮廓，原点为矩形中心
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <returns></returns>
        public static Profile CreateRectangle(double width, double height)
        {
            width = width / 2; height = height / 2;
            Line lin1 = Line.CreateBound(new XYZ(width, height, 0), new XYZ(-width, height, 0));
            Line lin2 = Line.CreateBound(new XYZ(-width, height, 0), new XYZ(-width, -height, 0));
            Line lin3 = Line.CreateBound(new XYZ(-width, -height, 0), new XYZ(width, -height, 0));
            Line lin4 = Line.CreateBound(new XYZ(width, -height, 0), new XYZ(width, height, 0));
            CurveLoop loop = new CurveLoop();
            loop.Append(lin1); loop.Append(lin2); loop.Append(lin3); loop.Append(lin4);
            return new Profile(loop);
        }
        /// <summary>
        /// 构建内接正多边形，原点为中心点，起点在X轴
        /// </summary>
        /// <param name="radius"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static Profile CreateRegularPolygon(double radius, int count)
        {
            XYZ point = new XYZ(radius, 0, 0);
            List<XYZ> points = new List<XYZ>() { point };
            for (int i = 1; i < count; i++)
            {
                double rad = i * 2 * Math.PI / count;
                points.Add(Autodesk.Revit.DB.Transform.CreateRotation(XYZ.BasisZ, rad).OfPoint(point));
            }
            CurveLoop loop = new CurveLoop();
            for (int i = 0; i < points.Count - 1; i++)
            {
                loop.Append(Line.CreateBound(points.ElementAt(i), points.ElementAt(i + 1)));
            }
            loop.Append(Line.CreateBound(points.ElementAt(points.Count - 1), points.ElementAt(0)));
            return new Profile(loop);
        }


        public double GetArea()
        {
            double area = GeometryCreationUtilities.CreateExtrusionGeometry(new List<CurveLoop>() { CurveLoop }, XYZ.BasisZ, 1).Faces.get_Item(1).Area;
            if (InnerProfile != null)
            {
                area -= GeometryCreationUtilities.CreateExtrusionGeometry(new List<CurveLoop>() { InnerProfile.CurveLoop }, XYZ.BasisZ, 1).Faces.get_Item(1).Area;
            }
            return area;
        }

        /// <summary>
        /// 获取中轴等分平面
        /// </summary>
        /// <returns></returns>
        public Plane GetUniformPlane()
        {
            Solid solid = GeometryCreationUtilities.CreateExtrusionGeometry(new List<CurveLoop>() { CurveLoop }, XYZ.BasisZ, 1);
            Face face = solid.Faces.OfType<PlanarFace>().First(m => m.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ.Negate()));
            return Plane.CreateByNormalAndOrigin(XYZ.BasisX, face.Evaluate(new UV(0.5, 0.5)));
        }
        /// <summary>
        /// 将轮廓按平面切割
        /// </summary>
        /// <param name="plane"></param>
        /// <returns></returns>
        public Profile CutWithPlane(Plane plane)
        {
            Solid solid = GeometryCreationUtilities.CreateExtrusionGeometry(new List<CurveLoop>() { CurveLoop }, XYZ.BasisZ, 1);
            Face face = BooleanOperationsUtils.CutWithHalfSpace(solid, plane).Faces.OfType<PlanarFace>().First(m => m.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ.Negate()));
            return new Profile(face.GetEdgesAsCurveLoops().First());
        }

        /// <summary>
        /// 与其它轮廓进行布尔操作
        /// </summary>
        /// <param name="loop"></param>
        /// <param name="operationsType"></param>
        /// <returns></returns>
        public IList<CurveLoop> ExecuteBooleanOperation(Profile profile, BooleanOperationsType operationsType)
        {
            Solid solid1 = GeometryCreationUtilities.CreateExtrusionGeometry(new List<CurveLoop>() { CurveLoop }, XYZ.BasisZ, 1);
            Solid solid2 = GeometryCreationUtilities.CreateExtrusionGeometry(new List<CurveLoop>() { profile.CurveLoop }, XYZ.BasisZ, 1);
            Solid solid = BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, operationsType);
            return solid.Faces.OfType<PlanarFace>().First(m => m.FaceNormal.IsAlmostEqualTo(XYZ.BasisZ.Negate())).GetEdgesAsCurveLoops();
        }

        #region 轮廓生成实体

        /// <summary>
        /// 创建向上拉伸的实体
        /// </summary>
        /// <param name="height"></param>
        /// <param name="materialId"></param>
        /// <returns></returns>
        public virtual Solid CreateExtrusionGeometry(double height, SolidOptions solidOptions)
        {
            List<CurveLoop> loops = new List<CurveLoop> { CurveLoop };
            if (InnerProfile != null)
            {
                loops.Add(InnerProfile.CurveLoop);
            }
            return GeometryCreationUtilities.CreateExtrusionGeometry(loops, XYZ.BasisZ, height, solidOptions);
        }



        /// <summary>
        /// 轮廓沿路径放样生成实体，轮廓垂直于地面
        /// </summary>
        /// <param name="path">直线时，直线方向不能垂直于地面</param>
        /// <param name="materialId"></param>
        public virtual Solid CreateLoftGeometry(Curve path, SolidOptions solidOptions)
        {
            Solid solid = CreateLoftGeometry(path, CurveLoop, solidOptions);
            if (InnerProfile != null)
            {
                Solid solid2 = CreateLoftGeometry(path, InnerProfile.CurveLoop, solidOptions);
                solid = BooleanOperationsUtils.ExecuteBooleanOperation(solid, solid2, BooleanOperationsType.Difference);//BooleanOperationsType枚举类型（一致，不同，相交）
            }
            return solid;
        }
        private Solid CreateLoftGeometry(Curve path, CurveLoop profile, SolidOptions solidOptions)
        {
            List<CurveLoop> loops = new List<CurveLoop>();
            for (int i = 0; i <= 10; i++)
            {
                double para = (double)i / 10;
                //CurveLoop.CreateViaTransform 创建一个新的曲线循环作为输入曲线循环的转换副本。 
                loops.Add(CurveLoop.CreateViaTransform(profile, TransformUtils.GetTransform(path, para)));
            }
            return GeometryCreationUtilities.CreateLoftGeometry(loops, solidOptions);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="plane"></param>
        /// <param name="solidOptions"></param>
        /// <returns></returns>
        public Solid CreateLoftGeometry(CurveLoop path, Plane plane, SolidOptions options)
        {
            XYZ normal = path.GetPlane().Normal;
            int count = 50;
            List<CurveLoop> loops = new List<CurveLoop>();
            for (int i = 0; i <= count; i++)
            {
                double para = (double)i / count;

                Transform transform = Transform.Identity;
                XYZ tangent = path.ComputeDerivatives(para).BasisX;
                transform.BasisX = normal;
                transform.BasisZ = tangent.Normalize();
                transform.BasisY = tangent.CrossProduct(normal).Normalize();

                XYZ point = path.Evaluate(para);
                double d1 = point.DistanceTo(plane.Origin) * Math.Cos((point - plane.Origin).AngleTo(plane.Normal));//点到plane的距离
                double d2 = d1 / Math.Cos(normal.AngleTo(plane.Normal));//path上的点沿法向量到plane的角点的距离

                transform.Origin = point + d2 * normal;

                loops.Add(CurveLoop.CreateViaTransform(this.CurveLoop, transform));
            }
            return GeometryCreationUtilities.CreateLoftGeometry(loops, options);
        }
        /// <summary>
        /// 轮廓沿路径放样生成实体,轮廓垂直于曲线
        /// </summary>
        /// <param name="path"></param>
        /// <param name="materialId"></param>
        /// <returns></returns>
        public virtual Solid CreateSweptGeometry(CurveLoop path, SolidOptions options)
        {
            int index = (int)Math.Floor(0.5 * (path.Count() - 1));
            Curve curve = path.ElementAt(index);
            double para_nor = 0.5;
            double para_raw = curve.ComputeRawParameter(para_nor);

            Transform transform = TransformUtils.GetTransformVertical(curve, para_nor);
            List<CurveLoop> loops = new List<CurveLoop>();
            if (transform.IsConformal)
            {
                loops.Add(CurveLoop.CreateViaTransform(CurveLoop, transform));
                if (InnerProfile != null)
                {
                    loops.Add(CurveLoop.CreateViaTransform(InnerProfile.CurveLoop, transform));
                }
            }
            return GeometryCreationUtilities.CreateSweptGeometry(path, index, para_raw, loops, options);
        }

        /// <summary>
        /// 轮廓绕Y轴旋转，轮廓必须在Y轴右侧，逆时针旋转(开始角度小于结束角度)
        /// </summary>
        /// <param name="startAngle"></param>
        /// <param name="endAngle"></param>
        /// <param name="materialId"></param>
        /// <returns></returns>
        public virtual Solid CreateRevolvedGeometry(double startAngle, double endAngle, SolidOptions options)
        {
            Frame frame = new Frame(XYZ.Zero, XYZ.BasisX, XYZ.BasisZ, -XYZ.BasisY);
            List<CurveLoop> loops = new List<CurveLoop>() { CurveLoop };
            if (InnerProfile != null)
            {
                loops.Add(InnerProfile.CurveLoop);
            }
            return GeometryCreationUtilities.CreateRevolvedGeometry(frame, loops, startAngle, endAngle, options);
        }

        #endregion



        public Solid Intersect(Solid solid1, Solid solid2)
        {
            return BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Intersect);
        }

        public Solid Union(Solid solid1, Solid solid2)
        {
            return BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Union);
        }
        public Solid Difference(Solid solid1, Solid solid2)
        {
            return BooleanOperationsUtils.ExecuteBooleanOperation(solid1, solid2, BooleanOperationsType.Difference);
        }

    }
}
